package datastructure.basic;

import datastructure.exception.EmptyListException;

import java.util.Objects;

public class LinkedList implements List {

    private Node head;
    private int size;

    public LinkedList() {
        head = new Node();
    }

    @Override
    public void add(Object o) {
        addLast(o);
    }

    @Override
    public void add(int index, Object o) {
        Node pre = findNode(index - 1);
        Node node = new Node();
        node.data = o;
        addNode(node, pre);
    }

    @Override
    public Object get(int index) {
        checkIndex(index);
        return findNode(index).data;
    }

    @Override
    public Object remove(int index) {
        checkIndex(index);
        Node pre = findNode(index - 1);
        Node removed = pre.next;
        removeNode(removed, pre);
        return removed.data;
    }

    @Override
    public int indexOf(Object o) {
        int i = 0;
        for (Node node = head.next; node != null; node = node.next, ++i) {
            if (node.data == null ? o == null : node.data.equals(o)) {
                return i;
            }
        }
        return -1;
    }

    @Override
    public int size() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size() == 0;
    }

    public void addFirst(Object o) {
        Node node = new Node();
        node.data = o;
        addNode(node, head);
    }

    public void addLast(Object o) {
        Node node = new Node();
        node.data = o;
        Node pre = findNode(size() - 1);
        addNode(node, pre);
    }

    public Object removeFirst() {
        if (size() == 0) {
            throw new EmptyListException();
        }
        Node removed = head.next;
        removeNode(head.next, head);
        return removed.data;
    }

    public Object removeLast() {
        if (size() == 0) {
            throw new EmptyListException();
        }
        return remove(size() - 1);
    }

    @Override
    public Iterator iterator() {
        return new Iterator() {
            Node node = head;

            @Override
            public boolean hasNext() {
                return node.next != null;
            }

            @Override
            public Object next() {
                node = node.next;
                return node.data;
            }
        };
    }

    private static class Node {
        Object data;
        Node next;
    }

    private Node findNode(int index) {
        if (index == -1) {
            return head;
        } else {
            checkIndex(index);
        }
        Node node = head.next;
        for (int i = 0; i < index; ++i) {
            node = node.next;
        }
        return node;
    }

    private void checkIndex(int index) {
        if (index >= size() || index < 0) {
            throw new IndexOutOfBoundsException(indexOutOfBoundMessage(index));
        }
    }

    private String indexOutOfBoundMessage(int index) {
        return "index: " + index + ", size: " + size();
    }

    private void addNode(Node node, Node pre) {
        node.next = pre.next;
        pre.next = node;
        size++;
    }

    private void removeNode(Node node, Node pre) {
        pre.next = node.next;
        node.next = null;
        size--;
    }

    //清空整条链，返回链中结点数量
    private int clearLink(Node start) {
        int count = 0;
        while (start != null) {
            Node node = start;
            start = start.next;
            node.data = null;
            node.next = null;
            count++;
        }
        return count;
    }

    /**
     * 把该链表逆置
     * 例如链表为 3->7->10 , 逆置后变为  10->7->3
     */
    public void reverse() {
        Stack stack = new Stack();
        for (Node node = head.next; node != null; node = node.next) {
            stack.push(node);
        }
        head.next = (Node) stack.peek();
        while (stack.size() > 1) {
            Node top = (Node) stack.pop();
            top.next = (Node) stack.peek();
        }
        ((Node) stack.peek()).next = null;
    }

    /**
     * 删除一个单链表的前半部分
     * 例如：list = 2->5->7->8 , 删除以后的值为 7->8
     * 如果list = 2->5->7->8->10 ,删除以后的值为7,8,10
     */
    public void removeFirstHalf() {
        int half = size() / 2;
        if (half > 1) {
            Node first = head.next;

            Node preHalf = findNode(half - 1);
            head.next = preHalf.next;
            preHalf.next = null;
            size -= clearLink(first);
        }
    }

    /**
     * 从第i个元素开始， 删除length 个元素 ， 注意i从0开始
     *
     * @param i
     * @param length
     */
    public void remove(int i, int length) {
        if (length <= 0) {
            return;
        }
        Node preI = findNode(i - 1);
        Node removeTo = findNode(i + length - 1);
        Node removeFrom = preI.next;
        preI.next = removeTo.next;
        removeTo.next = null;
        size -= clearLink(removeFrom);
    }

    /**
     * 假定当前链表和list均包含已升序排列的整数
     * 从当前链表中取出那些list所指定的元素
     * 例如当前链表 = 11->101->201->301->401->501->601->701
     * listB = 1->3->4->6
     * 返回的结果应该是[101,301,401,601]
     *
     * @param list
     */
    public Object[] getElements(LinkedList list) {
        Object[] result = new Object[list.size()];
        int count = 0;

        Node nodeI = head.next;
        Node nodeJ = list.head.next;
        for (int i = 0; nodeI != null && nodeJ != null; ++i) {
            int compare = i - (int) nodeJ.data;
            if (compare == 0) {
                result[count] = nodeI.data;
                count++;
                nodeI = nodeI.next;
                nodeJ = nodeJ.next;
            } else if (compare < 0) {
                nodeI = nodeI.next;
            } else {
                nodeJ = nodeJ.next;
                i--;
            }
        }
        Object[] trueResult = new Object[count];
        System.arraycopy(result, 0, trueResult, 0, count);
        return trueResult;
    }

    /**
     * 已知链表中的元素以值递增有序排列，并以单链表作存储结构。
     * 从当前链表中中删除在list中出现的元素
     *
     * @param list
     */

    public void subtract(LinkedList list) {
        Node pre = head;
        Node node = list.head.next;
        while (pre.next != null && node != null) {
            if ((int) pre.next.data < (int) node.data) {
                pre = pre.next;
            } else if ((int) pre.next.data > (int) node.data) {
                node = node.next;
            } else {
                removeNode(pre.next, pre);
            }
        }
    }

    /**
     * 已知当前链表中的元素以值递增有序排列，并以单链表作存储结构。
     * 删除表中所有值相同的多余元素（使得操作后的线性表中所有元素的值均不相同）
     */
    public void removeDuplicateValues() {
        Node node = head;
        while (node.next != null) {
            if (Objects.equals(node.data, node.next.data)) {
                removeNode(node.next, node);
            } else {
                node = node.next;
            }
        }
    }

    /**
     * 已知链表中的元素以值递增有序排列，并以单链表作存储结构。
     * 试写一高效的算法，删除表中所有值大于min且小于max的元素（若表中存在这样的元素）
     *
     * @param min
     * @param max
     */
    public void removeRange(int min, int max) {
        Node node = head;
        while (node.next != null) {
            int value = (int) node.next.data;
            if (value <= min) { // 还未进入范围
                node = node.next;
            } else if (value >= max) { // 超出范围，停止遍历
                break;
            } else { // 在范围内，删除之
                removeNode(node.next, node);
            }
        }
    }

    /**
     * 假设当前链表和参数list指定的链表均以元素依值递增有序排列（同一表中的元素值各不相同）
     * 现要求生成新链表C，其元素为当前链表和list中元素的交集，且表C中的元素有依值递增有序排列
     *
     * @param list
     */
    public LinkedList intersection(LinkedList list) {
        LinkedList result = new LinkedList();
        Node inThis = head.next;
        Node node = list.head.next;
        int[] temp = new int[Math.min(size(), list.size())];
        int count = 0;
        while (inThis != null && node != null) {
            int compare = (int) inThis.data - (int) node.data;
            if (compare < 0) {
                inThis = inThis.next;
            } else if (compare > 0) {
                node = node.next;
            } else {
                temp[count] = (int) node.data;
                count++;
                inThis = inThis.next;
                node = node.next;
            }
        }
        for (int i = count - 1; i >= 0; --i) {
            result.addFirst(temp[i]);
        }
        return result;
    }
}
